<?php

/**
 * OAuth登录组件
 * 
 * @author ShuangYa
 * @package EUser
 * @category Controller
 * @link http://www.sylingd.com/
 * @copyright Copyright (c) 2015-2016 ShuangYa
 * @license http://lab.sylingd.com/go.php?name=euser&type=license
 */

/**
 * 错误码：
 * invalid_request 缺少必要参数（invalud refresh token）
 * invalid_client client_id、client_secret无效（unknow client id）
 * invalid_grant AccessGrant无效（The provided authorization grant is revoked）
 * invalid_referer Referer不匹配（Invalid Referer）
 * invalid_redirect_uri RedirectURI不匹配（Invalid redirect uri）
 * invalid_token 无效的token
 */

namespace euser\controller;
use \Sy;
use \sy\base\Controller;
use \sy\lib\YHtml;
use \sy\lib\YCookie;
use \sy\lib\YSecurity;
use \euser\libs\Common;

class Oauth extends Controller {
	protected $isSDK = FALSE;
	public function __construct() {
		$this->u = Sy::service('user');
		$this->oauth = Sy::service('oauth');
		if (Sy::$httpRequest[$requestId]->header['x-method'] === 'SDK-HTTP') {
			//SDK请求
			Sy::$httpRequest[$requestId]->get = $_POST = $_REQUEST = json_decode(Sy::$httpRequest[$requestId]->rawContent(), 1);
			$this->isSDK = TRUE;
		}
	}
	protected function error($requestId, $code, $description, $isHTML = TRUE) {
		if ($isHTML) {
			Sy::setMimeType('html', $requestId);
			Sy::$httpResponse[$requestId]->write('<p>[' . $code . ']' . $description . '</p>');
		} else {
			Sy::setMimeType('json', $requestId);
			Sy::$httpResponse[$requestId]->write(Common::getAjax(['error' => $code, 'description' => $description]));
		}
	}
	/**
	 * 登录页面
	 * 参数：
	 * client_id 应用ID
	 * response_type 固定为code
	 * redirect_uri 回调地址，为oob则回调至EUser（用于无服务器应用）
	 * scope （非必需）授权内容
	 * state （非必需）原样回传，防止CSRF
	 * display （非必需）
	 */
	public function actionAuthorize($requestId) {
		//验证基本参数是否完备
		if (!isset(Sy::$httpRequest[$requestId]->get['response_type'])) {
			$this->error($requestId, 'invalid_request', 'Invalid response_type');
			return;
		}
		if (!isset(Sy::$httpRequest[$requestId]->get['redirect_uri'])) {
			$this->error($requestId, 'invalid_request', 'Invalid redirect_uri');
			return;
		}
		if (!isset(Sy::$httpRequest[$requestId]->get['client_id']) || !is_string(Sy::$httpRequest[$requestId]->get['client_id'])) {
			$this->error($requestId, 'invalid_client', 'unknow client id');
			return;
		}
		$client_id = Sy::$httpRequest[$requestId]->get['client_id'];
		//验证Client相关参数
		$client = $this->oauth->getClient($client_id);
		if ($client === NULL) {
			$this->error($requestId, 'invalid_client', 'unknow client id');
			return;
		}
		if (!empty($client['referer']) && !empty(Sy::$httpRequest[$requestId]->header['referer'])) {
			$referer_host = parse_url(Sy::$httpRequest[$requestId]->header['referer'], PHP_URL_HOST);
			$referers = $client['referer'];
			$referer_verify = FALSE;
			foreach ($referers as $referer) {
				if (preg_match($referer, $referer_host)) {
					$referer_verify = TRUE;
				}
			}
			if (!$referer_verify) {
				$this->error($requestId, 'invalid_referer', 'Invalid Referer');
				return;
			}
		}
		if (isset(Sy::$httpRequest[$requestId]->get['scope'])) {
			$scope = array_intersect(explode('|', Sy::$httpRequest[$requestId]->get['scope']), $client['scope']);
		} else {
			$scope = $client['scope'];
		}
		//展示登录页面
		if (!isset(Sy::$httpRequest[$requestId]->get['display']) || !in_array(Sy::$httpRequest[$requestId]->get['display'], ['default', 'mobile'])) {
			$display = 'default';
		} else {
			$display = Sy::$httpRequest[$requestId]->get['display'];
		}
		// $_csrf_token = \sy\lib\YSecurity::csrfGetHash();
		//展现页面
		Sy::setMimeType('html', $requestId);
		$data = ['redirect_uri' => Sy::$httpRequest[$requestId]->get['redirect_uri'], 'state' => Sy::$httpRequest[$requestId]->get['state'], 'client_id' => $client_id, 'scope' => $scope];
		$html = Sy::view('oauth/login/' . $display, $data);
		Sy::$httpResponse[$requestId]->write($html);
	}
	/**
	 * 通过AuthorizeCode换取AccessToken
	 * 参数：
	 * grant_type 固定为authorize_code
	 * client_id 应用ID
	 * client_secret 应用SecretKey
	 * redirect_uri 请求Authorize时携带
	 * code AuthorizeCode
	 */
	public function actionToken($requestId) {
		//允许跨域
		Sy::$httpResponse[$requestId]->header('Access-Control-Allow-Origin', '*');
		Sy::$httpResponse[$requestId]->header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
		Sy::$httpResponse[$requestId]->header('Access-Control-Allow-Headers', 'Cache-Control,X-Requested-With,Content-Type');
		//验证基本参数是否完备
		if (!isset(Sy::$httpRequest[$requestId]->get['grant_type'])) {
			$this->error($requestId, 'invalid_request', 'Invalid grant_type', FALSE);
			return;
		}
		if (!isset(Sy::$httpRequest[$requestId]->get['code']) || !is_string(Sy::$httpRequest[$requestId]->get['code'])) {
			$this->error($requestId, 'invalid_request', 'Invalid code', FALSE);
			return;
		}
		if (!isset(Sy::$httpRequest[$requestId]->get['client_id']) || !is_string(Sy::$httpRequest[$requestId]->get['client_id'])) {
			$this->error($requestId, 'invalid_client', 'unknow client id', FALSE);
			return;
		}
		if (!isset(Sy::$httpRequest[$requestId]->get['client_secret'])) {
			$this->error($requestId, 'invalid_client', 'unknow client secret', FALSE);
			return;
		}
		$code = trim(Sy::$httpRequest[$requestId]->get['code']);
		$authorize = $this->oauth->getAuthorizeCode($code);
		if ($authorize['expire'] <= Sy::$httpRequest[$requestId]->server['request_time']) {
			//AuthorizeCode过期
			$this->oauth->removeAuthorizeCode($code);
			$this->error($requestId, 'invalid_grant', 'The provided authorization grant is revoked', FALSE);
			return;
		}
		//检查redirect_uri
		if ($authorize['redirect_uri'] !== Sy::$httpRequest[$requestId]->get['redirect_uri']) {
			$this->error($requestId, 'invalid_redirect_uri', 'Invalid redirect uri', FALSE);
			return;
		}
		//检查Client
		if ($authorize['client'] !== Sy::$httpRequest[$requestId]->get['client_id']) {
			$this->error($requestId, 'invalid_client', 'Invalid client id', FALSE);
			return;
		}
		$client = $this->oauth->getClient(Sy::$httpRequest[$requestId]->get['client_id']);
		if ($client['secret'] !== Sy::$httpRequest[$requestId]->get['client_secret']) {
			$this->error($requestId, 'invalid_client', 'Invalid client secret', FALSE);
			return;
		}
		//生成AccessToken
		$access_token = $this->oauth->createAccessToken($id, Sy::$httpRequest[$requestId]->get['client_id'], $authorize['scope']);
		/**
		 * 返回：
		 * access_token
		 * scope 最终授权结果
		 * expires_in 有效期，单位：秒，默认为一个月
		 */
		Sy::setMimeType('json', $requestId);
		Sy::$httpResponse[$requestId]->write(Common::getAjax(['access_token' => $access_token, 'expires_in' => '2592000']));
	}
	/**
	 * 通过已登录的token换取AuthorizeCode
	 * token通过user/login接口获得，存放于Cookie，但不可被JS读取
	 */
	public function actionGetAuthorizeCode($requestId) {
		if (!isset($_POST['client_id']) || !is_string($_POST['client_id'])) {
			$this->error($requestId, 'invalid_client', 'unknow client id', FALSE);
			return;
		}
		$client_id = $_POST['client_id'];
		//验证Client相关参数
		$client = $this->oauth->getClient($client_id);
		if ($client === NULL) {
			$this->error($requestId, 'invalid_client', 'unknow client id');
			return;
		}
		if (FALSE === ($token = $this->u->verifyCookie($requestId))) {
			$this->error($requestId, 'invalid_token', 'The provided token grant is revoked', FALSE);
			return;
		}
		$authorizeCode = $this->oauth->createAccessToken($token['user'], $_POST['client_id'], array_intersect(explode('|', Sy::$httpRequest[$requestId]->get['scope']), $client['scope']));
		Sy::setMimeType('json', $requestId);
		Sy::$httpResponse[$requestId]->write(Common::getAjax(['AuthorizeCode' => $authorizeCode]));
	}
}
